﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;

namespace Noris.Tools.TraceVisualiser.Components
{
    #region DLabel : Label
    /// <summary>
    /// DLabel : Label
    /// </summary>
    public class DLabel : Label
    {
        public DLabel()
        {
        }
    }
    #endregion
    #region DTextBox : TextBox
    /// <summary>
    /// DTextBox : TextBox
    /// </summary>
    public class DTextBox : TextBox
    { }
    #endregion
    #region DCheckBox : CheckBox
    public class DCheckBox : CheckBox
    { }
    #endregion
    #region DComboBox : nabídka položek + DComboItem
    public class DComboBox : ComboBox
    {
        #region Konstrukce
        public DComboBox()
        {
            this.InitProperties();
        }
        public DComboBox(IEnumerable<object> items)
        {
            this.Items.AddRange(items.ToArray());
            this.InitProperties();
        }
        private void InitProperties()
        {
            this.DropDownStyle = ComboBoxStyle.DropDownList;
            this.SelectionChangeCommitted += new EventHandler(_SelectionChangeCommitted);
            this.SelectedValueChanged += new EventHandler(_SelectedValueChanged);
            this.SelectedIndexChanged += new EventHandler(_SelectedIndexChanged);
            this.TextChanged += new EventHandler(_TextChanged);
            this.TextUpdate += new EventHandler(_TextUpdate);
        }

        #endregion
        #region DComboItem : přidávání, aktuální položka, atd
        /// <summary>
        /// Přidá položku Combo
        /// </summary>
        /// <param name="text"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public DComboItem AddDComboItem(string text, object data)
        {
            return this._AddDComboItem(new DComboItem(text, data), false, null);
        }
        public DComboItem AddDComboItem(string text, object data, string selectName)
        {
            return this._AddDComboItem(new DComboItem(text, data), false, selectName);
        }
        public DComboItem AddDComboItem(string text, object data, bool selectFirst)
        {
            return this._AddDComboItem(new DComboItem(text, data), selectFirst, null);
        }
        /// <summary>
        /// Přidá položku Combo
        /// </summary>
        /// <returns></returns>
        public DComboItem AddDComboItem(DComboItem dItem)
        {
            return this._AddDComboItem(dItem, false, null);
        }
        public DComboItem AddDComboItem(DComboItem dItem, string selectName)
        {
            return this._AddDComboItem(dItem, false, selectName);
        }
        public DComboItem AddDComboItem(DComboItem dItem, bool selectFirst)
        {
            return this._AddDComboItem(dItem, selectFirst, null);
        }
        /// <summary>
        /// Naplní položky Combo (tj. předem smaže obsah Items).
        /// </summary>
        /// <param name="items"></param>
        public void FillDComboItems(IEnumerable<DComboItem> items)
        {
            this._AddDComboItems(items, true, false, null);
        }
        /// <summary>
        /// Naplní položky Combo (tj. předem smaže obsah Items).
        /// </summary>
        /// <param name="items"></param>
        public void FillDComboItems(IEnumerable<DComboItem> items, string selectName)
        {
            this._AddDComboItems(items, true, false, selectName);
        }
        /// <summary>
        /// Naplní položky Combo (tj. předem smaže obsah Items).
        /// První položku může vybrat jako SelectedItem, podle parametru selectFirst.
        /// </summary>
        /// <param name="items"></param>
        /// <param name="selectFirst"></param>
        public void FillDComboItems(IEnumerable<DComboItem> items, bool selectFirst)
        {
            this._AddDComboItems(items, true, selectFirst, null);
        }
        /// <summary>
        /// Přidá další položky Combo (tj. ponechá stávající obsah Items a přidá další).
        /// </summary>
        /// <param name="items"></param>
        public void AddDComboItems(IEnumerable<DComboItem> items)
        {
            this._AddDComboItems(items, false, false, null);
        }
        /// <summary>
        /// Přidá další položky Combo (tj. ponechá stávající obsah Items a přidá další).
        /// </summary>
        /// <param name="items"></param>
        public void AddDComboItems(IEnumerable<DComboItem> items, string selectName)
        {
            this._AddDComboItems(items, false, false, selectName);
        }
        /// <summary>
        /// Přidá další položky Combo (tj. ponechá stávající obsah Items a přidá další).
        /// První položku může vybrat jako SelectedItem, podle parametru selectFirst.
        /// </summary>
        /// <param name="items"></param>
        /// <param name="selectFirst"></param>
        public void AddDComboItems(IEnumerable<DComboItem> items, bool selectFirst)
        {
            this._AddDComboItems(items, false, selectFirst, null);
        }
        private void _AddDComboItems(IEnumerable<DComboItem> items, bool clear, bool selectFirst, string selectName)
        {
            if (clear)
                this.Items.Clear();

            foreach (DComboItem item in items)
                this._AddDComboItem(item, selectFirst, selectName);
        }
        private DComboItem _AddDComboItem(DComboItem dItem, bool selectFirst, string selectName)
        {
            this.Items.Add(dItem);
            if ((selectFirst && this.SelectedItem == null) || (!String.IsNullOrEmpty(selectName) && dItem.Text == selectName))
                this.SelectedItem = dItem;
            return dItem;
        }
        /// <summary>
        /// Aktuálně vybraná položka this.SelectedItem, přetypovaná na DComboItem.
        /// </summary>
        public DComboItem SelectedDItem
        {
            get
            {
                return this.SelectedItem as DComboItem;
            }
            set
            {
                if (value != null && this.Items.Contains(value))
                {
                    this.SelectedItem = value;
                    this.Text = value.Text;
                }
                else
                {
                    this.SelectedItem = null;
                    this.Text = "";
                }
            }
        }
        /// <summary>
        /// Pokusí se najít data v aktuálně vybraném objektu (this.SelectedDItem.Data),
        /// a pokud data jsou typu T, pak je uloží do parametru data a vrátí true. Jinak vrátí false.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool TryGetSelectedData<T>(out T data)
        {
            DComboItem selectedDItem = this.SelectedDItem;
            if (selectedDItem != null)
            {
                if (selectedDItem.Data is T)
                {
                    data = (T)selectedDItem.Data;
                    return true;
                }
            }
            data = default(T);
            return false;
        }
        #endregion
        #region Public eventy a jejich obsluha
        /// <summary>
        /// Událost volaná po změně vybrané nebo zadané hodnoty v ComboBoxu
        /// </summary>
        public event DComboBoxChangedHandler DComboBoxChanged;
        /// <summary>
        /// Potlačení provedení eventu DComboBoxChanged
        /// </summary>
        public bool DComboBoxChangedDisabled { get; set; }
        /// <summary>
        /// Selectuje položku podle jejího textu. Vrací true = byla nalezena. Proběhne event po změně.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public bool SelectDItem(string text)
        {
            bool select = false;
            if (text != null)
            {
                foreach (object item in this.Items)
                {
                    DComboItem dItem = item as DComboItem;
                    if (dItem != null)
                        select = (dItem.Text == text);
                    else
                        select = (item != null && item.ToString() == text);

                    if (select)
                    {
                        this.SelectedItem = item;
                        break;
                    }
                }
            }
            if (!select)
                this.SelectedItem = null;
            return select;
        }
        /// <summary>
        /// Selectuje položku, která první vyhovuje dané podmínce.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public bool SelectDItem(Func<DComboItem, bool> predicate)
        {
            object selectedOld = this.SelectedItem;
            bool select = false;
            if (predicate != null)
            {
                foreach (object item in this.Items)
                {
                    DComboItem dItem = item as DComboItem;
                    if (dItem != null)
                        select = predicate(dItem);

                    if (select)
                    {
                        this.SelectedItem = item;
                        break;
                    }
                }
            }
            if (!select)
                this.SelectedItem = null;
            
            object selectedNew = this.SelectedItem;
            if (!Object.ReferenceEquals(selectedNew, selectedOld))
                this.OnDComboBoxChanged(DComboBoxChangeMode.ItemChange);

            return select;
        }

        protected virtual void OnDComboBoxChanged(DComboBoxChangeMode changeMode)
        {
            this.OnDComboBoxChanged(changeMode, this.SelectedDItem, this.Text);
        }
        protected virtual void OnDComboBoxChanged(DComboBoxChangeMode changeMode, DComboItem item, string text)
        {
            if (!this.DComboBoxChangedDisabled && this.DComboBoxChanged != null)
                this.DComboBoxChanged(this, new DComboBoxChangedArgs(changeMode, item, text));
        }
        void _TextUpdate(object sender, EventArgs e)
        {
        }
        void _TextChanged(object sender, EventArgs e)
        {
            if (this.DropDownStyle == ComboBoxStyle.DropDown || this.DropDownStyle == ComboBoxStyle.Simple)
            {   // V režimu DropDown dojde mimo jiné k tomuto eventu po každé změně textu (výběr z listu, změna textu = okamžitě)
                DComboBoxChangeMode changeMode = this._LastChange;
                if (changeMode != DComboBoxChangeMode.ItemChange)
                    changeMode = DComboBoxChangeMode.TextChange;
                this.OnDComboBoxChanged(changeMode, this.SelectedDItem, this.Text);
                this._LastChange = DComboBoxChangeMode.None;
            }
        }
        void _SelectionChangeCommitted(object sender, EventArgs e)
        {
            //this._LastChange = DComboBoxChangeMode.ItemChange;
            //if (this.DropDownStyle == ComboBoxStyle.DropDownList)
            //{   // V režimu DropDownList dojde pouze k tomuto eventu, a to po výběru nové položky v Listu a zavření Listu (List ještě svítí),
            //    // anebo v zavřeném listu po změně vybrané položky pomocí šipky dolů/nahoru:
            //    // V režimu DropDown (kdy je možno text vepisovat) reagujeme v eventu _TextChanged a odesíláme událost.
            //    this.OnDComboBoxChanged(DComboBoxChangeMode.ItemChange, this.SelectedDItem, this.Text);
            //}
        }
        void _SelectedValueChanged(object sender, EventArgs e)
        {
        }
        void _SelectedIndexChanged(object sender, EventArgs e)
        {
            this._LastChange = DComboBoxChangeMode.ItemChange;
            if (this.DropDownStyle == ComboBoxStyle.DropDownList)
            {   // V režimu DropDownList dojde pouze k tomuto eventu, a to po výběru nové položky v Listu a zavření Listu (List ještě svítí),
                // anebo v zavřeném listu po změně vybrané položky pomocí šipky dolů/nahoru:
                // V režimu DropDown (kdy je možno text vepisovat) reagujeme v eventu _TextChanged a odesíláme událost.
                this.OnDComboBoxChanged(DComboBoxChangeMode.ItemChange, this.SelectedDItem, this.Text);
            }
        }
        private DComboBoxChangeMode _LastChange;
        #endregion
    }
    /// <summary>
    /// EventArgs pro DComboBox
    /// </summary>
    public class DComboBoxChangedArgs : EventArgs
    {
        public DComboBoxChangedArgs(DComboBoxChangeMode changeMode, DComboItem item, string text)
        {
            this.ChangeMode = changeMode;
            this.Item = item;
            this.Text = text;
        }
        /// <summary>
        /// Režim změny
        /// </summary>
        public DComboBoxChangeMode ChangeMode { get; private set; }
        /// <summary>
        /// Aktuální vybraná položka
        /// </summary>
        public DComboItem Item { get; private set; }
        /// <summary>
        /// Aktuální text
        /// </summary>
        public string Text { get; private set; }
    }
    public delegate void DComboBoxChangedHandler(object sender, DComboBoxChangedArgs args);
    public enum DComboBoxChangeMode
    {
        None = 0,
        TextChange,
        ItemChange

    }
    #endregion
    #region DComboItem : jedna položka nabídky DCombo, obsahuje text a objekt
    public class DComboItem
    {
        public DComboItem() { }
        public DComboItem(string text, object data)
        {
            this.Text = text;
            this.Data = data;
        }
        public override string ToString()
        {
            return this.Text;
        }
        public string Text { get; set; }
        public object Data { get; set; }
    }
    #endregion
    #region DButton
    public class DButton : Button
    {
        public Image ImageEnabled
        {
            get { return this._ImageEnabled; }
            set { this._ImageEnabled = value; if (this.Enabled) this._RefreshImage(); } 
        }
        private Image _ImageEnabled;
        public Image ImageDisabled
        {
            get { return this._ImageDisabled; }
            set { this._ImageDisabled = value; if (!this.Enabled) this._RefreshImage(); }
        }
        private Image _ImageDisabled;
        protected override void OnEnabledChanged(EventArgs e)
        {
            base.OnEnabledChanged(e);
            this._RefreshImage();
        }
        private void _RefreshImage()
        {
            if (this.Enabled)
            {
                if (this._ImageEnabled != null)
                    this.Image = this._ImageEnabled;
            }
            else
            {
                if (this._ImageDisabled != null)
                    this.Image = this._ImageDisabled;
            }
        }
    }
    #endregion
    #region DProgressPanel : panel obsahující DProgressBar, text nad progresem, text obsahující procenta přes progress
    /// <summary>
    /// DProgressPanel : panel obsahující DProgressBar, text nad progresem, text obsahující procenta přes progress
    /// </summary>
    public class DProgressPanel : DPanel
    {
        #region Konstrukce
        public DProgressPanel()
        {
            this.Initialize();
        }
        private void Initialize()
        {
            this.SuspendLayout();

            this.MainLabel = new DLabel() { TextAlign = ContentAlignment.MiddleCenter, AutoSize = false, Text = "" };
            this.ProgressLabel = new DLabel() { TextAlign = ContentAlignment.MiddleCenter, AutoSize = false, Text = "", BackColor = Color.Transparent };
            this.DProgressBar = new DProgressBar();

            this.Controls.Add(this.MainLabel);
            this.Controls.Add(this.ProgressLabel);
            this.Controls.Add(this.DProgressBar);

            this.SizeChanged += new EventHandler(DProgressPanel_SizeChanged);
            this.LayoutInners();

            this.ResumeLayout(false);
        }
        private void DProgressPanel_SizeChanged(object sender, EventArgs e)
        {
            this.LayoutInners();
        }
        private void LayoutInners()
        {
            Rectangle a = this.ClientRectangle;
            int l = a.Left;
            int w = a.Width;
            int h = a.Height;
            int t = h - this.DProgressBar.Height;
            this.MainLabel.Bounds = new Rectangle(l, a.Top, w, t - a.Top);
            this.DProgressBar.Bounds = new Rectangle(l, t, w, this.DProgressBar.Height);
            this.ProgressLabel.Bounds = this.DProgressBar.Bounds;
            this.ProgressLabel.Visible = false;
            this.ProgressLabel.BackColor = Color.Transparent;
        }
        private DProgressBar DProgressBar;
        private DLabel MainLabel;
        private DLabel ProgressLabel;
        #endregion
        #region Public property
        [Browsable(true)]
        [Category("DProgress")]
        public int ProgressHeight { get { return this.DProgressBar.Height; } set { this.DProgressBar.Height = value; this.LayoutInners(); } }
        [Browsable(true)]
        [Category("DProgress")]
        public string MainText { get { return this.MainLabel.Text; } set { this.MainLabel.Text = value; } }
        [Browsable(true)]
        [Category("DProgress")]
        public string ProgressText { get { return this.ProgressLabel.Text; } set { this.ProgressLabel.Text = value; } }
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ProgressPartial ProgressPartial { get { return this.DProgressBar.ProgressPartial; } }
        public void Reset()
        {
            if (this.InvokeRequired)
                this.BeginInvoke(new Action(this.Reset));
            else
            {
                this.ProgressPartial.Reset();
                this.MainText = "";
                this.ProgressText = "";
            }
        }
        #endregion
    }
    #endregion
    #region DProgressBar
    public class DProgressBar : ProgressBar
    {
        public DProgressBar()
        {
            this.Style = ProgressBarStyle.Continuous;
            this._ProgressPartial = new ProgressPartial(this);
            this.Minimum = 0;
            this.Maximum = 1000;
        }
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ProgressPartial ProgressPartial { get { return this._ProgressPartial; } }
        private ProgressPartial _ProgressPartial;
        public float DValue
        {
            get { return this._DValueGet(); }
            set { this._DValueSet(value); }
        }

        private float _DValueGet()
        {
            if (this.InvokeRequired)
                return (float)this.Invoke(new Func<float>(this._DValueGet));
            else
            {
                float val = (float)(this.Value - this.Minimum) / (float)(this.Maximum - this.Minimum);
                return (val < 0f ? 0f : (val > 1f ? 1f : val));
            }
        }

        private void _DValueSet(float value)
        {
            if (this.InvokeRequired)
                this.BeginInvoke(new Action<float>(this._DValueSet), value);
            else
            {
                float val = (value < 0f ? 0f : (value > 1f ? 1f : value));
                this.Value = (int)(Math.Round((val * (float)(this.Maximum - this.Minimum)), 0));
            }
        }
    }
    #endregion
    #region ProgressPartial : data pro řízení pro dělitelného progresu
    /// <summary>
    /// Objekt pro řízení Progresu s libovolně dělitelnými úseky
    /// </summary>
    public class ProgressPartial
    {
        public ProgressPartial(DProgressBar progressBar)
        {
            this._DProgressBar = progressBar;
            this.Reset();
        }
        /// <summary>
        /// Vrátí nový progres, který bude pokrývat danou část progresu this
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public ProgressPartial CreateSubProgress(decimal from, decimal to)
        {
            return new ProgressPartial(from, to, this);
        }
        public ProgressPartial(decimal parentFrom, decimal parentTo)
        {
            this.ParentPart = null;
            this._ValueParentSet(parentFrom, parentTo);
            this._ValueCurrentSet(0m, 1m);
        }
        public ProgressPartial(decimal parentFrom, decimal parentTo, ProgressPartial parentPart)
        {
            this.ParentPart = parentPart;
            this._ValueParentSet(parentFrom, parentTo);
            this._ValueCurrentSet(0m, 1m);
        }
        public ProgressPartial(decimal parentFrom, decimal parentTo, decimal valueFrom, decimal valueTo, ProgressPartial parentPart)
        {
            this.ParentPart = parentPart;
            this._ValueParentSet(parentFrom, parentTo);
            this._ValueCurrentSet(valueFrom, valueTo);
        }
        public event ProgressDivideChangeHandler ParentValueChanged;
        protected virtual void OnParentValueChanged()
        {
            if (this.ParentValueChanged != null)
                this.ParentValueChanged(this, new ProgressDivideChangeArgs(this._ParentValue));
        }
        public ProgressPartial ParentPart { get; private set; }
        public decimal ParentFrom { get; private set; }
        public decimal ParentTo { get; private set; }
        public decimal ParentValue { get { return _ParentValue; } }
        public decimal ValueFrom { get { return _ValueFrom; } set { _ValueFrom = value; this._ValueFromToSet(); } }
        public decimal ValueTo { get { return _ValueTo; } set { _ValueTo = value; this._ValueFromToSet(); } }
        public decimal Value { get { return _Value; } set { this._ValueSet(value); } }
        public void Reset()
        {
            this._ValueParentSet(0m, 1m);
            this._ValueCurrentSet(0m, 1m);
        }
        private DProgressBar _DProgressBar;
        private decimal _ParentValue;
        private decimal _ParentLength;
        private decimal _ValueLength;
        private decimal _ValueFrom;
        private decimal _ValueTo;
        private decimal _Value;

        private void _ValueParentSet(decimal parentFrom, decimal parentTo)
        {
            this.ParentFrom = parentFrom;
            this.ParentTo = (parentTo < parentFrom ? parentFrom : parentTo);
            this._ParentLength = parentTo - parentFrom;
            this._ParentValue = parentFrom;
        }

        private void _ValueCurrentSet(decimal valueFrom, decimal valueTo)
        {
            this._ValueFrom = valueFrom;
            this._ValueTo = valueTo;
            this._Value = valueFrom;
            this._ValueFromToSet();
        }
        /// <summary>
        /// Po změně hodnoty ValueFrom nebo ValueTo
        /// </summary>
        private void _ValueFromToSet()
        {
            if (_ValueTo < _ValueFrom) _ValueTo = _ValueFrom;
            _ValueLength = _ValueTo - _ValueFrom;

            this._ValueSet(_Value);
        }
        /// <summary>
        /// Po změně hodnoty Value
        /// </summary>
        /// <param name="value"></param>
        private void _ValueSet(decimal value)
        {
            // this.Value:
            if (value < this._ValueFrom)
                this._Value = this._ValueFrom;
            else if (value > this._ValueTo)
                this._Value = this._ValueTo;
            else
                this._Value = value;

            // this.ParentValue:
            decimal parentValue = 0m;
            if (this._ValueLength == 0m)
                parentValue = this.ParentFrom;
            else
                parentValue = this.ParentFrom + this._ParentLength * ((this._Value - this.ValueFrom) / this._ValueLength);

            // OnChange:
            if (this._ParentValue == parentValue)
            {
                if (this._DProgressBar != null)
                    this._DProgressBarSetValue();
                return;
            }

            this._ParentValue = parentValue;
            this.OnParentValueChanged();

            if (this._DProgressBar != null)
                this._DProgressBarSetValue();

            // this.ParentValue to Parent.Value:
            if (this.ParentPart != null)
                this.ParentPart.Value = this._ParentValue;
        }

        private void _DProgressBarSetValue()
        {
            decimal currentValue = Math.Round((_ProgressDivisor * this._ParentValue), 0) / _ProgressDivisor;
            if (currentValue != this._DProgressLastValue)
            {
                this._DProgressBar.DValue = (float)currentValue;
                this._DProgressLastValue = currentValue;
            }
        }
        private const decimal _ProgressDivisor = 500m;
        private decimal _DProgressLastValue = 0m;
    }
    public delegate void ProgressDivideChangeHandler(object sender, ProgressDivideChangeArgs args);
    public class ProgressDivideChangeArgs : EventArgs
    {
        public ProgressDivideChangeArgs(decimal value)
        {
            this.Value = value;
        }
        public decimal Value { get; private set; }
    }
    #endregion
    #region DListBox : ListBox
    public class DListBox : ListBox
    { }
	#endregion
    #region DListView : ListView
    public class DListView : ListView
    { }
    #endregion
}
